package compilarclassejava;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.LinkedList;

public class MudarSystemInEOutDeClasseJava 
{
	private static int contadorVariaveisUsadasNosSystemOuts;
	private static int contadorVariaveisUsadasNosSystemIns;
	private static String nomeScannerEncontradoEmArquivoLido;
	private static String enderecoCopiaClasseJava;
	
	
	public MudarSystemInEOutDeClasseJava()
	{
		contadorVariaveisUsadasNosSystemOuts = 0;
		contadorVariaveisUsadasNosSystemIns = 0;
		nomeScannerEncontradoEmArquivoLido = "nomeScannerTestTable";
	}
	
	public static String getEnderecoCopiaClasseJava()
	{
		return enderecoCopiaClasseJava;
	}
	
	public static void mudarEnderecoCopiaClasseJava(String novoEndereco)
	{
		enderecoCopiaClasseJava = novoEndereco;
	}
	
	
	public static void fazerUpdateArquivoJava(String enderecoArquivoJava)
	{
		
		System.out.println("iniciou metodo fazer update");
		
		//resetar a classe primeiro, afinal ela vai complar uma nova classe...
		contadorVariaveisUsadasNosSystemOuts = 0;
		contadorVariaveisUsadasNosSystemIns = 0;
		nomeScannerEncontradoEmArquivoLido = "nomeScannerTestTable";
		
		boolean pacoteFoiEncontrado = false; //durante a leitura do arquivo, se acharmos o pacote, esse boolean muda
		
		LinkedList<String> linhasLidasDoJava = new LinkedList<String>();
		@SuppressWarnings("unused")
		File arquivo = new File(enderecoArquivoJava);
		try{
			
			  //agora vamos guardar os dados do objeto caso suite de testes em um .txt
		      InputStream file = new FileInputStream(enderecoArquivoJava);
		     
		      InputStreamReader leLinhasArquivoJava = new InputStreamReader(file);
		      BufferedReader bufferLeLinhasArquivo = new BufferedReader(leLinhasArquivoJava);
		     
		      try{
		    	  String linhaLidaArquivoJava = bufferLeLinhasArquivo.readLine();
		    	  while(linhaLidaArquivoJava != null)
		    	  {
		    		  System.out.println("linha lida do arqivo java=" + linhaLidaArquivoJava);
		    		  if(linhaLidaArquivoJava.contains("System.out"))
			    	  {
		    			  String comandoJavaLido = "";
		    			  boolean pontoEVirgulaFoiAchado = false;
		    			  while(pontoEVirgulaFoiAchado == false)
		    			  {
		    				  for(int i = 0; i < linhaLidaArquivoJava.length(); i++)
		    				  {
		    					  String umCaractere = String.valueOf(linhaLidaArquivoJava.charAt(i));
		    					  comandoJavaLido = comandoJavaLido + umCaractere;
		    					  System.out.println("caractere percorrido:" + umCaractere);
		    					  if(umCaractere.compareTo(";") == 0)
		    					  {
		    						  System.out.println("ponto e virgula foi achado!");
		    						  pontoEVirgulaFoiAchado = true;
		    						  break;
		    					  }
		    				  }
		    				  
		    				  if(pontoEVirgulaFoiAchado == false)
		    				  {
		    					  //vamos ter de ler outra linha...
		    					  linhaLidaArquivoJava = bufferLeLinhasArquivo.readLine();
		    				  }
		    				  
		    				 
		    			  }
		    			  System.out.println("comando java foi achado");
		    			  String oqueEstahDentroDosParentesesDoComando = pegarTudoQueEstahDentroDosParentesDoComandoJavaLido(comandoJavaLido);
	    				  String novoComandoJava = "";
	    				  
	    				  System.out.println("o que estah dentro dos parenteses:" + oqueEstahDentroDosParentesesDoComando);
	    				  novoComandoJava = substituirOQueEstahDentroDoParentesesPorVariavel(oqueEstahDentroDosParentesesDoComando);
	    				  
	    				  
	    				  System.out.println("novo comando java:" + novoComandoJava);
			    		  //achamos a linha que contem o booleanao que procuramos!
			    		  linhaLidaArquivoJava = novoComandoJava;
			    	  }
		    		  else if(linhaLidaArquivoJava.contains("Scanner"))
		    		  {
		    			  pegarNomeDoScannerLeDoTeclado(linhaLidaArquivoJava);
		    			  linhaLidaArquivoJava = "";
		    			  
		    		  }
		    		  else if(linhaLidaArquivoJava.contains(nomeScannerEncontradoEmArquivoLido + ".next()") 
		    				  || linhaLidaArquivoJava.contains(nomeScannerEncontradoEmArquivoLido + ".nextLine()") )
		    		  {
		    			  linhaLidaArquivoJava = substituirLeituraDoTecladoPorVariavel(linhaLidaArquivoJava);
		    		  }
		    		  else if(linhaLidaArquivoJava.contains("package") == true &  pacoteFoiEncontrado == false)
		    		  {
		    			  //achamos o pacote do arquivo .java! tiraremos esse pacote!
		    			  linhaLidaArquivoJava = "";
		    			  pacoteFoiEncontrado = true;
		    		  }
		    		  else if(linhaLidaArquivoJava.contains("import java.util.Scanner") == true)
		    		  {
		    			  linhaLidaArquivoJava = "";
		    		  }
		    		  
		    		  linhasLidasDoJava.add(linhaLidaArquivoJava);
		    		  linhaLidaArquivoJava = bufferLeLinhasArquivo.readLine();
			    	 
		    	  }
		    	  System.out.println("_______________________n~ao ha mais linhas para ler do arquivo java");
		    	
		    	
		      }
		      finally{
		         
		    	  System.out.println("fechando buffers leitura");
		    	  bufferLeLinhasArquivo.close();
		    	  leLinhasArquivoJava.close();
		    	  file.close();
		    	  linhasLidasDoJava = declararVariaveisSystemParaArquivoJava(linhasLidasDoJava);
		    	  System.out.println("agora ele vai escrever o arquivo java de nv");
		    	  
		    	  escreverOQueAcabouDeLer(linhasLidasDoJava, enderecoArquivoJava);
		    	  
		    	  System.out.println("terminou fazer update");
		      }
		    }  
		    catch(Exception ex){
		      ex.printStackTrace();
		    }
		
	}
	
	public static void escreverOQueAcabouDeLer(LinkedList<String> linhasQueAcabouDeLer, String enderecoDoArquivoJava)
	{
		System.out.println("iniciou metodo escrever");
		enderecoCopiaClasseJava =
				acharEnderecoParaEscreverCopiaDoArquivoJava(enderecoDoArquivoJava);
		
		try{
			
			  //agora vamos guardar os dados do objeto caso suite de testes em um .txt
		      OutputStream file = new FileOutputStream( enderecoCopiaClasseJava);
		     
		      OutputStreamWriter escreveNovoArquivoJava = new OutputStreamWriter(file);
		      BufferedWriter bufferEscreveLinhasArquivo = new BufferedWriter(escreveNovoArquivoJava);
		     
		      try{
		    	  for(int i = 0; i < linhasQueAcabouDeLer.size(); i++)
		    	  {
		    		  String linhaParaEscrever = linhasQueAcabouDeLer.get(i);
		    		  bufferEscreveLinhasArquivo.write(linhaParaEscrever + "\n");
		    	  }
		    	
		    	
		      }
		      finally{
		         
		    	  bufferEscreveLinhasArquivo.close();
		    	  escreveNovoArquivoJava.close();
		    	  file.close();
		    	 
		      }
		    }  
		    catch(Exception ex){
		      ex.printStackTrace();
		    }
		System.out.println("terminou metodo escrever");
	}
	
	private static String acharEnderecoParaEscreverCopiaDoArquivoJava(String enderecoDoArquivoJava)
	{
		System.out.println("////// endereco do arquivo java === " + enderecoDoArquivoJava);
		 //vamos guardar a copia do arquivo java na pasta backups testboot. se ela nao existe, deve ser criada.
		File diretorioPastaDosTestes = new File("backupsTestable");
		if(diretorioPastaDosTestes.exists() != true)
		{
			  //nao ha esse diretorio? entao vamos cria-lo
			  diretorioPastaDosTestes.mkdir();
		}
		String [] enderecoSeparadoDoArquivoJava = enderecoDoArquivoJava.split("/");
		String arquivoJava = enderecoSeparadoDoArquivoJava[enderecoSeparadoDoArquivoJava.length - 1];
		String enderecoParaEscreverCopia = "backupsTestable/" + arquivoJava;
		
		return enderecoParaEscreverCopia;
		
	}
	
	private static void pegarNomeDoScannerLeDoTeclado(
			String linhaLidaArquivoJava) 
	{
		//encontramos um scanner que o programa ira usar para ler coisas do teclado
		  String auxilarLeituraLinhaLidaArquivoJava = "";
		  for(int k = 0; k < linhaLidaArquivoJava.length(); k++)
		  {
			  auxilarLeituraLinhaLidaArquivoJava =
					  auxilarLeituraLinhaLidaArquivoJava + linhaLidaArquivoJava.charAt(k);
			  if(auxilarLeituraLinhaLidaArquivoJava.contains("Scanner"))
			  {
				  String auxNomeDaVariavelScanner = "";
				  String umCaractere = "";
				  for(int l = k + 1; l < linhaLidaArquivoJava.length(); l++)
				  {
					  umCaractere = String.valueOf(linhaLidaArquivoJava.charAt(l));
					  if(umCaractere.compareTo(" ") != 0 & umCaractere.compareTo("=") != 0)
					  {
						  auxNomeDaVariavelScanner = auxNomeDaVariavelScanner + umCaractere;
					  }
					  else if(umCaractere.compareTo("=") == 0)
					  {
						  nomeScannerEncontradoEmArquivoLido = auxNomeDaVariavelScanner;
						  break;
					  }
					  
				  }
				  
				  break;
			  }
			  
		  }
	}
	
	private static String pegarTudoQueEstahDentroDosParentesDoComandoJavaLido(String comandoJavaLido)
	{
		System.out.println("comando java com System.out:" + comandoJavaLido);
		String tudoDentroDoParenteses = "";
		for(int i = 0; i < comandoJavaLido.length(); i++)
		{
			 String umCaractere = String.valueOf(comandoJavaLido.charAt(i));
			 if(umCaractere.compareTo("(") == 0)
			 {
				 int posicaoUltimoParenteses = comandoJavaLido.lastIndexOf(")");
				 tudoDentroDoParenteses = comandoJavaLido.substring(i + 1, posicaoUltimoParenteses);
				 
				 return tudoDentroDoParenteses;
				 /*for(int j = comandoJavaLido.length(); j > 0; j--)
				 {
					 System.out.println("j=" + j);
					 System.out.println("char at j=" + comandoJavaLido.charAt(j));
					 String umCaractereFim = "" + comandoJavaLido.charAt(j);
					 System.out.println("um caractere fim:" + umCaractereFim);
					 if(umCaractereFim.compareTo(")") == 0)
					 {
						 System.out.println("achou fim parenteses");
						 tudoDentroDoParenteses = comandoJavaLido.substring(i + 1, j - 1);
						 return tudoDentroDoParenteses;
					 }
				 }*/
				 //System.out.println("terminou loop int j");
				
			 }
		}
		
		return tudoDentroDoParenteses;
	}
	
	private static String substituirOQueEstahDentroDoParentesesPorVariavel(String oqueEstahDentroDosParenteses)
	{
		
		String novoComando = "variavelTestTableSaida" + contadorVariaveisUsadasNosSystemOuts + "= String.valueOf(" + oqueEstahDentroDosParenteses + ");";
		contadorVariaveisUsadasNosSystemOuts = contadorVariaveisUsadasNosSystemOuts + 1;
		return novoComando;
		
		
	}
	
	private static String substituirLeituraDoTecladoPorVariavel(String linhaLidaArquivoJava)
	{
		String novoComando;
		novoComando = linhaLidaArquivoJava.replace(nomeScannerEncontradoEmArquivoLido + ".next()", "variavelTestTableEntrada" + contadorVariaveisUsadasNosSystemIns);
		novoComando = linhaLidaArquivoJava.replace(nomeScannerEncontradoEmArquivoLido + ".nextLine()", "variavelTestTableEntrada" + contadorVariaveisUsadasNosSystemIns);
		contadorVariaveisUsadasNosSystemIns = contadorVariaveisUsadasNosSystemIns + 1;
		return novoComando;
	}
	
	/**
	 * declara variaveis para os System.in e System.out que tinha no main
	 * @param linhasLidasArquivoJava as linhas do arquivo .java previamente lido
	 * @return
	 */
	private static LinkedList<String> declararVariaveisSystemParaArquivoJava(LinkedList<String> linhasLidasArquivoJava)
	{
		System.out.println("comecou declararVariaveisSystemParaArquivoJava");
		for(int i = 0; i < linhasLidasArquivoJava.size(); i++)
		{
			String linhaDoArquivoJava = linhasLidasArquivoJava.get(i);
			if(linhaDoArquivoJava.contains("public static void main"))
			{
				//declararemos as variaveis antes do main
				//primeiro as variaveis dos System.in
				int indiceDeclaracaoVariaveis = i;
				
				String linhaDeclaracaoVariaveisSystemIn = "";
				
				if(contadorVariaveisUsadasNosSystemIns > 0)
				{
					//ha variaveis que devemos declarar no arquivo que vamos criar
					linhaDeclaracaoVariaveisSystemIn = "public static String ";
				}
				for(int j = 0; j < contadorVariaveisUsadasNosSystemIns; j++)
				{
					if(j == contadorVariaveisUsadasNosSystemIns - 1)
					{
						//ultimo elemento do loop
						linhaDeclaracaoVariaveisSystemIn = linhaDeclaracaoVariaveisSystemIn + "variavelTestTableEntrada" + j + ";\n";
						
					}
					else
					{
						linhaDeclaracaoVariaveisSystemIn = linhaDeclaracaoVariaveisSystemIn + "variavelTestTableEntrada" + j + ",";
					}
				}
				
				System.out.println("variaveis declaradas do system.in:" + linhaDeclaracaoVariaveisSystemIn);
				
				//agora as variaveis do System.out
				String linhaDeclaracaoVariaveisSystemOut = "";
				
				if(contadorVariaveisUsadasNosSystemOuts > 0)
				{
					//ha variaveis que devemos declarar no arquivo que vamos criar
					linhaDeclaracaoVariaveisSystemOut = "public static String ";
				}
				for(int k = 0; k < contadorVariaveisUsadasNosSystemOuts; k++)
				{
					if(k == contadorVariaveisUsadasNosSystemOuts - 1)
					{
						//ultimo elemento do loop
						linhaDeclaracaoVariaveisSystemOut = linhaDeclaracaoVariaveisSystemOut +
								"variavelTestTableSaida" + k + ";\n";
					}
					else
					{
						linhaDeclaracaoVariaveisSystemOut = linhaDeclaracaoVariaveisSystemOut +
								"variavelTestTableSaida" + k + ",";
					}
				}
				//agora, inserir nas linhas do arquivo java as declaracoes, sendo antes do public staic void main
				System.out.println("variaveis declaradas do system.in:" + linhaDeclaracaoVariaveisSystemOut);
				//vamos ter de definir getters e setters para essas variaveis tb
				String declaracaoGetters = "";
				for(int l = 0; l < contadorVariaveisUsadasNosSystemIns; l++)
				{
					declaracaoGetters = declaracaoGetters + "public String getVariavelTestTableEntrada" + l + "(){ return variavelTestTableEntrada" +l + "; }\n";
				}
				for(int m = 0; m < contadorVariaveisUsadasNosSystemOuts; m++)
				{
					declaracaoGetters = declaracaoGetters + "public String getVariavelTestTableSaida" + m + "(){ return variavelTestTableSaida" +m + "; }\n";
				}
				
				String declaracaoSetters = "";
				for(int n = 0; n < contadorVariaveisUsadasNosSystemIns; n++)
				{
					declaracaoSetters = declaracaoSetters + "public void setVariavelTestTableEntrada" + n + "(String novoValor){ variavelTestTableEntrada" + n + " = novoValor ; }\n";
				}
				for(int o = 0; o < contadorVariaveisUsadasNosSystemOuts; o++)
				{
					declaracaoSetters = declaracaoSetters + "public void setVariavelTestTableSaida" + o + "(String novoValor){ variavelTestTableSaida" +o + " = novoValor ; }\n";
				}
				
				linhasLidasArquivoJava.add(indiceDeclaracaoVariaveis, linhaDeclaracaoVariaveisSystemIn + linhaDeclaracaoVariaveisSystemOut + declaracaoGetters + declaracaoSetters);
				break;
			}
		}
		
		System.out.println("terminou declararVariaveisSystemParaArquivoJava");
		return linhasLidasArquivoJava;
	}

	public static int getContadorVariaveisUsadasNosSystemOuts() {
		return contadorVariaveisUsadasNosSystemOuts;
	}

	public static int getContadorVariaveisUsadasNosSystemIns() {
		return contadorVariaveisUsadasNosSystemIns;
	}
	
	
	
	

}
